package com.ruoyi.configuration.service.impl;

import com.common.util.ListConvertMap;
import com.common.util.TimeUtil;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.CurrentWeekRange;
import com.ruoyi.system.mapper.DashboardMapper;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;
import java.time.*;
import java.time.chrono.ChronoLocalDate;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAdjusters;
import java.time.temporal.TemporalUnit;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
public class DashboardServiceImpl {

	@Autowired
	private DashboardMapper dashboardMapper;

	static TemporalUnit[] temporalUnit = new TemporalUnit[] { ChronoUnit.DAYS, ChronoUnit.MONTHS, ChronoUnit.YEARS };

	static String[] format = new String[] { "yyyy-MM-dd", "yyyy-MM", "yyyy" };

	interface TemporalContract extends Temporal {
		default String format(DateTimeFormatter formatter) {
			Objects.requireNonNull(formatter, "formatter");
			return formatter.format(this);
		}
	}

	interface ComputerTime {
		List<Temporal> result(LocalDate start, LocalDate end, long i, int type);
	}

	static ComputerTime[] functions = new ComputerTime[] { (LocalDate start, LocalDate end, long time, int type) -> {

		// 计算两个日期之间的天数差异
		int index = type - 1;
		TemporalUnit temporalUnitItem = temporalUnit[index];

		long daysBetween = temporalUnitItem.between(start, end);

		List<Temporal> dateList = new ArrayList<>();
		for (long i = 0; i <= daysBetween; i++) {
			dateList.add(start.plusDays(i));
		}

		return dateList;

	}, (LocalDate start, LocalDate end, long time, int type) -> {
		List<Temporal> yearMonthList = new ArrayList<>();
		YearMonth currentYearMonth = YearMonth.from(start);
		while (!currentYearMonth.isAfter(YearMonth.from(end))) {
			yearMonthList.add(currentYearMonth);
			currentYearMonth = currentYearMonth.plusMonths(1);
		}
		return yearMonthList;
	}, (LocalDate start, LocalDate end, long time, int type) -> {

		List<Temporal> yearMonthList = new ArrayList<>();
		Year currentYearMonth = Year.from(start);
		while (!currentYearMonth.isAfter(Year.from(end))) {
			yearMonthList.add(currentYearMonth);
			currentYearMonth = currentYearMonth.plusYears(1);
		}
		return yearMonthList;
	} };

	/**
	 * @param type      1 按日 2 按月 3 按年
	 * @param timestamp
	 * @param endTime
	 * @return
	 */
	public List<Temporal> dateTime(long timestamp, long endTime, int type) {

		// 将时间戳转换为LocalDate对象
		LocalDate startDate = Instant.ofEpochSecond(timestamp).atZone(ZoneId.systemDefault()).toLocalDate();
		LocalDate endDate = Instant.ofEpochSecond(endTime).atZone(ZoneId.systemDefault()).toLocalDate();

		if (1 > type || 3 <= type) {
			return new ArrayList<Temporal>(0);
		}

		int index = type - 1;

		// 计算两个日期之间的天数差异
		// 生成包含每天日期的数组

		return functions[index].result(startDate, endDate, index, type);

	}

	public AjaxResult getData(ReqD reqD) {
		LocalDateTime startDateTime = null;
		LocalDateTime endDateTime = null;
		// 1服务订单
		Long start = reqD.getStart();
		
		LocalDate endTime = LocalDate.now();
		
		LocalDate startTime = endTime.minusDays(6);
//		LocalDate[] weekStartAndEnd = CurrentWeekRange.weekStartAndEnd(now);
		
		if (null == reqD.start) {
			reqD.start = startTime.atTime(0, 0, 0).toEpochSecond(ZoneOffset.of("+8"));
		}
		
		if (null == reqD.end) {
			reqD.end = endTime.atTime(23, 59, 59).toEpochSecond(ZoneOffset.of("+8"));
		}
		
		if (start != null) {
			startDateTime = LocalDateTime.ofInstant(Instant.ofEpochSecond(start), ZoneId.systemDefault());
		} else {
			startDateTime = startTime.atTime(0, 0, 0);
		}
		Long end = reqD.getEnd();
		if (end != null) {
			endDateTime = LocalDateTime.ofInstant(Instant.ofEpochSecond(end), ZoneId.systemDefault());
		} else {
			
			endDateTime = endTime.atTime(23, 59, 59);
			
		}
		List<TrendResDTO> data = dashboardMapper.getTrend1(reqD);
		
		Integer type = reqD.getType();
		
		parseZeroDate(start, end, data, type);

		
		data = timeCompletion(startDateTime, endDateTime, data, TrendResDTO.class, type);
		// 2售后
		List<TrendResDTO> data2 = dashboardMapper.getTrend2(reqD);
		
		
		parseZeroDate(start, end, data2, type);
		
		data2 = timeCompletion(startDateTime, endDateTime, data2, TrendResDTO.class, type);
		// 3消费金额，预存金额
		List<TrendResDTO> data3 = dashboardMapper.getTrend3(reqD);

		parseZeroDate(start, end, data3, type);

		data3 = timeCompletion(startDateTime, endDateTime, data3, TrendResDTO.class, type);

		// 充值
		List<TrendResDTO> recharges =  dashboardMapper.getTrend4(reqD);
		
		parseZeroDate(start, end, recharges, type);

		recharges = timeCompletion(startDateTime, endDateTime, recharges, TrendResDTO.class, type);

		Map<String, Object> map = new HashMap<>();
		map.put("expectedData", data);
		map.put("type", 1);

		Map<String, Object> map1 = new HashMap<>();
		map1.put("expectedData", data2);
		map1.put("type", 2);

		Map<String, Object> map2 = new HashMap<>();
		map2.put("expectedData", data3);
		map2.put("actualData", recharges);
		map2.put("type", 3);

		Map<String, Object> rt = new HashMap<>();
		rt.put("a", map);
		rt.put("b", map1);
		rt.put("c", map2);

		return AjaxResult.success(rt);
	}

	/**
	 * 
	 * @param start     开始日期
	 * @param end       结束日期
	 * @param recharges 数据
	 * @param type      1 按日 2 按月 3 按年
	 */
	protected void parseZeroDate(Long start, Long end, List<TrendResDTO> recharges, Integer type) {

		if (null == type) {
			type = 1;
		}

		List<Temporal> dateTime;
		if (null == start) {
			long[] dayStartOfEndByTimStamp = TimeUtil.getDayStartOfEndByTimStamp(System.currentTimeMillis());
			dateTime = dateTime(dayStartOfEndByTimStamp[0], dayStartOfEndByTimStamp[1], type);
		} else {
			dateTime = dateTime(start, end, type);
		}

		if (1 > type || 3 <= type) {
			return;
		}

		DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format[type - 1]);

		Map<String, TrendResDTO> listToMap = ListConvertMap.listToMap(recharges, TrendResDTO::getTime);

		TrendResDTO resDTO;

		int size = dateTime.size();

		String format;

		for (int i = 0; i < size; i++) {

			Temporal temporal = dateTime.get(i);
			format = formatter.format(temporal);

			resDTO = listToMap.get(format);

			if (null != resDTO) {
				continue;
			}
			resDTO = new TrendResDTO();
			resDTO.setTime(format);
			resDTO.setNum(BigDecimal.ZERO);
			recharges.add(resDTO);
		}

	}

	public AjaxResult getData2(ReqD reqD) {

		List<Data2> r = dashboardMapper.getData2(reqD);
		return AjaxResult.success(r);
	}

	public AjaxResult getData3(ReqD reqD) {
		Map<String, Object> rt = new HashMap<>();
		// 用户
		rt.put("a", new Data3(dashboardMapper.getUserCount(reqD)));
		// 订单
		rt.put("b", new Data3(dashboardMapper.getOrderCount1(reqD)));
		// 售后
		rt.put("c", new Data3(dashboardMapper.getOrderCount2(reqD)));
		// 消费金额
		rt.put("d", new Data3(dashboardMapper.getAmount1(reqD)));
		// 到账金额
		rt.put("e", new Data3(dashboardMapper.getAmount2(reqD)));

		return AjaxResult.success(rt);
	}

	public static <T extends TrendResDTO> List<T> timeCompletion(LocalDateTime startDate, LocalDateTime endDate,
			List<T> datas, Class<T> dtoClass, Integer type) {
		if (startDate == null && endDate == null) {
			return datas;
		}
		List<String> months = getDateIntervalList(startDate, endDate);
		if (2 == type) {
			months = getMonthsBetween(startDate, endDate);
		}
		if (3 == type) {
			months = getYearDifferenceList(startDate, endDate);
		}
		datas = datas.stream().filter(d -> d != null && StringUtils.isNotBlank(d.getTime()))
				.collect(Collectors.toList());
		Set<String> existingTimes = datas.stream().map(TrendResDTO::getTime).collect(Collectors.toSet());
		List<T> newDatas = months.stream().filter(month -> !existingTimes.contains(month))
				.collect(Collectors.mapping(month -> createInstance(dtoClass, month), Collectors.toList()));
		datas.addAll(newDatas);
		datas.sort(Comparator.comparing(TrendResDTO::getTime));
		return datas;
	}

	private static List<String> getDateIntervalList(LocalDateTime startDateTime, LocalDateTime endDateTime) {
		List<String> dateIntervalList = new ArrayList<>();

		// 将 LocalDateTime 转换为 LocalDate
		LocalDate startDate = startDateTime.toLocalDate();
		LocalDate endDate = endDateTime.toLocalDate();

		// 添加日区间到列表
		while (!startDate.isAfter(endDate)) {
			dateIntervalList.add(startDate.format(DateTimeFormatter.ISO_LOCAL_DATE));
			startDate = startDate.plusDays(1);
		}

		return dateIntervalList;
	}

	public static List<String> getMonthsBetween(LocalDateTime startDate, LocalDateTime endDate) {
		List<String> months = new ArrayList<>();
		DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM");
		LocalDateTime currentMonth = startDate.withDayOfMonth(1).withHour(0).withMinute(0).withSecond(0).withNano(0);
		while (!currentMonth.isAfter(endDate)) {
			months.add(currentMonth.format(formatter));
			currentMonth = currentMonth.plusMonths(1);
		}
		return months;
	}

	private static List<String> getYearDifferenceList(LocalDateTime startDate, LocalDateTime endDate) {
		List<String> yearDifferenceList = new ArrayList<>();

		// 计算年份差异
		int startYear = startDate.getYear();
		int endYear = endDate.getYear();

		// 添加年份到列表
		for (int year = startYear; year <= endYear; year++) {
			yearDifferenceList.add(String.valueOf(year));
		}
		return yearDifferenceList;
	}

	private static <T extends TrendResDTO> T createInstance(Class<T> dtoClass, String time) {
		try {
			T newDto = dtoClass.getDeclaredConstructor().newInstance();
			newDto.setTime(time);
			return newDto;
		} catch (InstantiationException | IllegalAccessException | NoSuchMethodException
				| InvocationTargetException e) {
			e.printStackTrace();
		}
		return null;
	}

	public static class ReqD {
		private Integer type = 1;
		private String[] time1;
		private Long start;
		private Long end;

		public Long getStart() {
			if (type == 2) {
				// 月
				if (time2 != null) {
					String m = time2[0];
					String[] split = m.split("-");
					LocalDateTime startDate = LocalDateTime.of(Integer.valueOf(split[0]), Integer.valueOf(split[1]), 1,
							0, 0);
					LocalDate localDate = startDate.toLocalDate();
					this.start = localDate.atStartOfDay().atZone(ZoneId.systemDefault()).toEpochSecond();
				}
			} else {
				if (time1 != null) {
					String m = time1[0];
					String[] split = m.split("-");
					LocalDateTime startDate = LocalDateTime.of(Integer.valueOf(split[0]), Integer.valueOf(split[1]),
							Integer.valueOf(split[2]), 0, 0, 0);
					LocalDate localDate = startDate.toLocalDate();
					this.start = localDate.atStartOfDay().atZone(ZoneId.systemDefault()).toEpochSecond();
				}
			}
			return start;
		}

		public void setStart(Long start) {
			this.start = start;
		}

		public Long getEnd() {
			if (type == 2) {
				// 月
				if (time2 != null) {
					String m = time2[1];
					String[] split = m.split("-");
					LocalDateTime end = LocalDateTime
							.of(Integer.valueOf(split[0]), Integer.valueOf(split[1]), 1, 23, 59, 59)
							.with(TemporalAdjusters.lastDayOfMonth());
					this.end = end.atZone(ZoneId.systemDefault()).toEpochSecond();
				}
			} else {
				if (time1 != null) {
					String m = time1[1];
					String[] split = m.split("-");
					LocalDateTime end = LocalDateTime.of(Integer.valueOf(split[0]), Integer.valueOf(split[1]),
							Integer.valueOf(split[2]), 23, 59, 59);
					this.end = end.atZone(ZoneId.systemDefault()).toEpochSecond();
				}
			}
			return end;
		}

		public void setEnd(Long end) {
			this.end = end;
		}

		public Integer getType() {
			return type;
		}

		public void setType(Integer type) {
			this.type = type;
		}

		public String[] getTime1() {
			return time1;
		}

		public void setTime1(String[] time1) {
			this.time1 = time1;
		}

		public String[] getTime2() {
			return time2;
		}

		public void setTime2(String[] time2) {
			this.time2 = time2;
		}

		private String[] time2;

	}

	public static class TrendResDTO {

		private String time;

		public BigDecimal getNum() {
			return this.num;
		}

		public void setNum(BigDecimal num) {
			this.num = num;
		}

		private BigDecimal num = BigDecimal.ZERO;

		public void setTime(String time) {
			this.time = time;
		}

		public String getTime() {
			return time;
		}
	}

	public static class Data2 {
		private String name;
		private Integer value;

		public Data2() {
		}

		public Data2(String name, Integer value) {
			this.name = name;
			this.value = value;
		}

		public String getName() {
			return name;
		}

		public void setName(String name) {
			this.name = name;
		}

		public Integer getValue() {
			return value;
		}

		public void setValue(Integer value) {
			this.value = value;
		}
	}

	public static class Data3 {
		private Integer startValue = 0;
		private BigDecimal endValue = BigDecimal.ZERO;
		private Integer animationDuration = 3600;

		public Data3() {
		}

		public Data3(BigDecimal endValue) {
			this.endValue = endValue;
		}

		public Integer getStartValue() {
			return startValue;
		}

		public void setStartValue(Integer startValue) {
			this.startValue = startValue;
		}

		public BigDecimal getEndValue() {
			return endValue;
		}

		public void setEndValue(BigDecimal endValue) {
			this.endValue = endValue;
		}

		public Integer getAnimationDuration() {
			return animationDuration;
		}

		public void setAnimationDuration(Integer animationDuration) {
			this.animationDuration = animationDuration;
		}
	}
}
